/**
 * TouchpadTouchListener.java
 *
 * Created by Andrew Moss on 2 Nov 2010
 *
 * Contains a class for handling touch events on the touchpad.
 */

package com.mossdev.android.moteapp.listeners;

import com.mossdev.android.moteapp.Config;
import com.mossdev.android.moteapp.MoteApp;
import com.mossdev.android.moteapp.comms.Commands;
import com.mossdev.android.moteapp.comms.ServerConnectionException;

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

/**
 * A class for dealing with the users touch events on the touchpad.
 * It will interpret the user's input as directional IR events and
 * OK commands. In this way it will act as a navigation tool.
 * 
 * Horiz scrolling is deliberately slower than vertical scrolling.
 * Locks into horizontal or vertical scrolling mode, requiring greater
 * movement in the opposite direction to change. This is meant to prevent
 * accidental jumps between menus in mythtv et al.
 * 
 * Currently assumes one pointer. Multitouch is not supported.
 * 
 * @author Andrew Moss
 */
public class TouchpadTouchListener implements OnTouchListener {

	//Constants
	//------------------------
	
	private static final long MAX_TAP_LENGTH = 500; //Max length of a tap
	
	//Used to map the magnitude of changes in the coord system to direction ir events
	private static final float SPEED_MODIFIER = 0.04f; 
	private static final float X_EXTRA_SPEED_MODIFIER = 0.24f; //Horizontal scrolling is slower 
	
	//Amount taken from the magnitude of change in the direction not currently 'locked'
	private static final int MAGNITUDE_HAMPER = 6; 
	
	
	//Globals
	//------------------------
	
	private boolean movement = false; //record whether any movement seen
	
	//Previous coords of a pointer during touch events
	private float lastX = 0f;
	private float lastY = 0f;
	
	private boolean y_mode = false; //seen some y axes scrolling, resist x movement
	private boolean x_mode = false; //seen some x axes scrolling, resist y movement
	
	
	//Methods
	//------------------------

	@Override
	/**
	 * Called when touch event occurs on view this is used as a listener for.
	 */
	public boolean onTouch(View v, MotionEvent event) {
		
		float currentX = 0f;
		float currentY = 0f;

		// Get the type of action driving the callback
		switch (event.getAction()) {
		
		case (MotionEvent.ACTION_DOWN): {

			// reset movement indicator
			this.movement = false;
			
			//Record the coords for movement events
			this.lastX = event.getX();
			this.lastY = event.getY();
			
			//reset historical scrolling direction
			this.y_mode = false;
			this.x_mode = false;

			break;
		}
		
		case (MotionEvent.ACTION_UP): {
			
			//reset historical scrolling direction
			this.y_mode = false;
			this.x_mode = false;

			// if no movement since down event
			if (!this.movement) {
				// if time since down event not too long
				if ((event.getDownTime() + MAX_TAP_LENGTH) > event.getEventTime()) {
					// tap event
					try {
						MoteApp.servercomms.sendCommand(Commands.SELECT);
					} catch (ServerConnectionException e) {
						Log.e(Config.LogTag, e.err_msg);
					}

				}
			}
			break;
		}
		
		case (MotionEvent.ACTION_MOVE): {

			//get coords
			currentX = event.getX();
			currentY = event.getY();
			
			//Ignore out of view touches
			if (currentX < 0 || currentY < 0) break;
			
			// Get Magnitude of movement in x and y
			float mag_y = currentY - this.lastY;
			float sane_mag_y = mag_y;
			if (mag_y < 0) sane_mag_y = mag_y *-1;
			
			float mag_x = currentX - this.lastX;
			float sane_mag_x = mag_x;
			if (mag_x < 0) sane_mag_x = mag_x *-1;
			
			//Ignore really small movements, let the difference build up
			if (Math.round(sane_mag_x*SPEED_MODIFIER*X_EXTRA_SPEED_MODIFIER) == 0 
					&& Math.round(sane_mag_y*SPEED_MODIFIER) == 0)
				break;
			
			//log the movement
			if(!this.movement) this.movement = true;
			
			// We only move parrallel to the axes.
			// Give weight to historical scrolling direction
			if (this.y_mode) {
				if (sane_mag_y >= (sane_mag_x - MAGNITUDE_HAMPER)) { //magic number here wants defined as a constant
					verticalScroll(mag_y);
				} else {
					horizontalScroll(mag_x);
				}
			} else if (this.x_mode) {
				if (sane_mag_x >= (sane_mag_y - MAGNITUDE_HAMPER)) {
					horizontalScroll(mag_x);
				} else {
					verticalScroll(mag_y);
				}
			} else {
				if (sane_mag_y >= sane_mag_x) {
					// Vertical scrolling
					verticalScroll(mag_y);
				} else {
					// Horizontal scrolling
					horizontalScroll(mag_x);
				}
			}
			
			//Record the coords for movement events
			this.lastX = event.getX();
			this.lastY = event.getY();
			
			break;
		}
		
		default: {
			//do nothing
			break;
		}
		}
		
		return true;
	}
	
	private void verticalScroll(float magnitude_y_change) {
		this.y_mode = true;
		this.x_mode = false;

		if (magnitude_y_change > 0) {
			// Scrolling down
			try {
				MoteApp.servercomms.sendCommand(Commands.DOWN);
			} catch (ServerConnectionException e) {
				Log.e(Config.LogTag, e.err_msg);
			}
		} else {
			// Scrolling Up
			try {
				MoteApp.servercomms.sendCommand(Commands.UP);
			} catch (ServerConnectionException e) {
				Log.e(Config.LogTag, e.err_msg);
			}
		}
	}
	
	private void horizontalScroll(float magnitude_x_change) {
		this.x_mode = true;
		this.y_mode = false;

		if (magnitude_x_change > 0) {
			// Scrolling right
			try {
				MoteApp.servercomms.sendCommand(Commands.RIGHT);
			} catch (ServerConnectionException e) {
				Log.e(Config.LogTag, e.err_msg);
			}
		} else {
			// Scrolling left
			try {
				MoteApp.servercomms.sendCommand(Commands.LEFT);
			} catch (ServerConnectionException e) {
				Log.e(Config.LogTag, e.err_msg);
			}
		}
	}

}
